import cgi
import pickle
import logging

from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext import webapp
from datetime import datetime
from datetime import date
from datetime import timedelta

import time
import re

from google.appengine.ext.webapp.util import run_wsgi_app

FLAG_LAST_STAT_DATE = "LAST_STAT_DATE"
TOTAL_IMAGE_COUNT = 36
TOTAL_HOT_ITEM_COUNT = 26
FLAG_HOT_ITEM = "FLAG_HOT_ITEM"
FLAG_RANDOM_ITEM = "FLAG_RANDOM_ITEM"
FLAG_NEWLY_LISTED_ITEM = "FLAG_NEWLY_LISTED_ITEM"
FLAG_ENDING_SOONEST_ITEM = "FLAG_ENDING_SOONEST_ITEM"
FLAG_UPDATING = "FLAG_UPDATING" 
max_updating_duration = 600 # updating time 10 mins

#########################
## if the items are being updated ##
#########################
def is_updating():
    if memcache.get(FLAG_UPDATING) == None:
        return False
    else:
        return True

class ItemModel(db.Model):
    store_id = db.StringProperty()
    ebay_id = db.StringProperty()
    image_url = db.LinkProperty()
    view_count = db.IntegerProperty()
    watch_count = db.IntegerProperty()
    sold_count = db.IntegerProperty()
    update_time = db.DateTimeProperty(auto_now=True)
    meta_category = db.IntegerProperty()
    score = db.FloatProperty()
    format = db.IntegerProperty() # 1: auction 2: fixed price
    start_time = db.DateTimeProperty()
    end_time = db.DateTimeProperty()
    duration = db.IntegerProperty() # in seconds
    ready_to_delete = db.BooleanProperty()

class DailyClickNumberStatModel(db.Model):
    number = db.IntegerProperty()
    count = db.IntegerProperty()

class DailyClickIdStatModel(db.Model):
    id = db.StringProperty()
    count = db.IntegerProperty()

class DailyUserStatModel(db.Model):
    user = db.StringProperty()
    visit = db.IntegerProperty()
    click = db.IntegerProperty()

class DailyStatReportModel(db.Model):
    total_count = db.IntegerProperty()
    stat_data = db.ListProperty(str)
    type = db.IntegerProperty() # 1: number stat 2: ebay id stat
    stat_date = db.DateTimeProperty(auto_now=True)

class DailyUserReportModel(db.Model):
    unique_user_count = db.IntegerProperty()
    snapshot_user_count = db.IntegerProperty()
    percent = db.FloatProperty()
    stat_date = db.DateTimeProperty(auto_now=True)

# get item from db
#def get_item(number): # number start from 1
#    item = db.Query(ItemModel).filter('format = ', 2).order('-score').fetch(limit=1, offset=number-1)[0]
#    return item

# get item from memcache
def get_item_new(number): # number start from 1
    if number <= TOTAL_HOT_ITEM_COUNT:
        item_key = memcache.get(get_hot_item_flag(number-1))
        if item_key == None:
            build_hot_items()
            item_key = memcache.get(get_hot_item_flag(number-1))
    else:
        item_key = memcache.get(get_newly_listed_item_flag(number-TOTAL_HOT_ITEM_COUNT-1))
        if item_key == None:
            build_newly_listed_items()
            item_key = memcache.get(get_newly_listed_item_flag(number-TOTAL_HOT_ITEM_COUNT-1))
    return db.get(item_key)

def get_user(request):
    return request.remote_addr

def get_hot_item_flag(item_index): # item_index start from zero
    return FLAG_HOT_ITEM + str(item_index)
    
def get_newly_listed_item_flag(item_index): # item_index start from zero
    return FLAG_NEWLY_LISTED_ITEM + str(item_index)

def build_hot_items():
    items = db.Query(ItemModel).filter('format = ', 2).order('-score').fetch(limit=TOTAL_IMAGE_COUNT)
    for i,  item in enumerate(items):
        memcache.set(get_hot_item_flag(i),  item.key())
    return True
    
def build_newly_listed_items():
    items = db.Query(ItemModel).filter('format = ', 2).order('duration').fetch(limit=TOTAL_IMAGE_COUNT)
    for i,  item in enumerate(items):
        memcache.set(get_newly_listed_item_flag(i),  item.key())
    return True

def get_hot_item(item_index): # item_index start from zero
    item_key = memcache.get(get_hot_item_flag(item_index))
    return db.get(item_key)

def stat_update(number, ebay_id, request): # number is int
	
	# update number stat
	result = db.GqlQuery("SELECT * FROM DailyClickNumberStatModel WHERE number = :1", number).get()
	if result == None:
		result = DailyClickNumberStatModel(number=number, count=0)
	result.count += 1
	result.put()
	
	# update ebay id stat
	result = db.GqlQuery("SELECT * FROM DailyClickIdStatModel WHERE id = :1", ebay_id).get()
	if result == None:
		result = DailyClickIdStatModel(id=ebay_id, count=0)
	result.count += 1
	result.put()
	
	# update snapshot user count
	user = get_user(request)
	result = db.GqlQuery("SELECT * FROM DailyUserStatModel WHERE user = :1", user).get()
	if result == None:
		result = DailyUserStatModel(user=user, visit=0, click=0)
	result.click += 1
	result.put()

def daily_report():
	last_stat_time = memcache.get(FLAG_LAST_STAT_DATE)
	right_now = datetime.now()
	
	if last_stat_time == None:
		memcache.set(FLAG_LAST_STAT_DATE, right_now)
		return
	
	if  (right_now - last_stat_time).days < 1:
		return
	
	log_stat()
	log_user()
	memcache.set(FLAG_LAST_STAT_DATE, last_stat_time+timedelta(days=1))
	
def user_update(request):
	user = get_user(request)
	result = db.GqlQuery("SELECT * FROM DailyUserStatModel WHERE user = :1", user).get()
	if result == None:
		result = DailyUserStatModel(user=user, visit=0, click=0)
	result.visit += 1
	result.put()
	
def log_stat():
	stat_list = []
	total = 0
	
	results = db.Query(DailyClickNumberStatModel).order("-count")
	for i in results:
		stat_list.append(str(i.number) + ":" + str(i.count))
		total += i.count
	dailyStat = DailyStatReportModel(total_count=total, stat_data=stat_list, type=1) # number stat
	dailyStat.put()
	db.delete(results)
	
	stat_list = []
	results = db.Query(DailyClickIdStatModel).order("-count")
	for i in results:
		stat_list.append(i.id + ":" + str(i.count))
	dailyStat = DailyStatReportModel(total_count=total, stat_data=stat_list, type=2) # ebay id stat
	dailyStat.put()
	db.delete(results)
	
def log_user():
	unique_user_count = db.Query(DailyUserStatModel).count()
	snapshot_user_count = db.Query(DailyUserStatModel).filter("click > ", 0).count()
	
	if unique_user_count == 0:
		percent = 0.0
	else:
		percent = float(snapshot_user_count)/float(unique_user_count)
	dailyUser = DailyUserReportModel(unique_user_count=unique_user_count, snapshot_user_count=snapshot_user_count, percent=percent)
	dailyUser.put()
	
	entry_per_round = 200
	while db.Query(DailyUserStatModel).count() > 0:
		results = db.Query(DailyUserStatModel).fetch(limit=entry_per_round)
		db.delete(results)

class MainPage(webapp.RequestHandler):
	def get(self):
		return self.response.out.write("hello")
	
class RankedImage(webapp.RequestHandler):
	def get(self):
		#try:
		number = self.request.get('no')
		if number == None or number == "":
			return self.response.out.write('')
		item = get_item_new(int(number))
		if int(number) == 1:
			user_update(self.request)
		if int(number) == TOTAL_IMAGE_COUNT:
			daily_report()
		return self.redirect(item.image_url)
		#except:
		#	return self.response.out.write('ERROR')

class RankedItem(webapp.RequestHandler):
	def get(self):
		#try:
		number = self.request.get('no')
		if number == None or number == "":
			return self.response.out.write('')
		item = get_item_new(int(number))
			
		# update stat
		stat_update(number=int(number), ebay_id=item.ebay_id, request=self.request)
			
		return self.redirect("http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item=" + item.ebay_id)
		#except:
		#	return self.response.out.write('ERROR')
 
class DeleteOldItems(webapp.RequestHandler):
    def get(self):
        while True:
            results = db.Query(ItemModel).filter('ready_to_delete = ',  True).fetch(limit=50, offset=0)
            if len(results) == 0:
                break
            db.delete(results)
        return self.response.out.write("OK")

def get_datetime(sec):
    return datetime(*time.localtime(sec)[:6])


class UpdateItem(webapp.RequestHandler):
    def get(self):
        #try:
        store_id = self.request.get("store_id")
        ebay_id = self.request.get("ebay_id")
        image_url = self.request.get("image_url")
        view_count = self.request.get("view_count")
        watch_count = self.request.get("watch_count")
        sold_count = self.request.get("sold_count")
        meta_category = self.request.get("meta_category")
        format = self.request.get("format")
        start_time = self.request.get("start_time")
        end_time = self.request.get("end_time")

        item = ItemModel()
        item.store_id = store_id
        item.ebay_id = ebay_id
        item.image_url = image_url
        item.view_count = int(view_count)
        item.watch_count = int(watch_count)
        item.sold_count = int(sold_count)
        item.meta_category = int(meta_category)
        item.format = int(format)
        item.start_time = get_datetime(int(start_time))
        item.end_time = get_datetime(int(end_time))
        item.duration = int(time.mktime(time.localtime())) - int(start_time)
        item.score = float(item.sold_count * 2 + item.watch_count) * 10000 / float(item.duration)
        item.ready_to_delete = False
        item.put()
        return self.response.out.write('OK')

        #except:
        #	return self.response.out.write('ERROR')

def getReferer(request):
	referer = request.headers.get("Referer")
	if referer == None:
		referer = request.headers.get("referer")
	return referer

def get_ebay_id_from_referer(referer):
	p = re.compile("item=\d{12}")
	m = p.search(referer)
	if m == None:
		return None
	item_id = m.group()
	p = item_id.split('=')
	if len(p) != 2:
		return None
	return p[1]

def getActiveItem(ebay_id):
	return db.GqlQuery("SELECT * FROM ItemModel WHERE ebay_id = :1", ebay_id).get()

def getBinItem(store_id):
	return db.GqlQuery("SELECT * FROM ItemModel WHERE store_id = :1 and format = 2", store_id).get()

def getAllItemsUrl():
	return "http://shop.ebay.com/merchant/augfirstsepend"

def getBinPic():
	#return "http://lh5.ggpht.com/_83T8aHezR6o/Sdg71WNTQsI/AAAAAAAADy0/-3mSEkiizhE/BIN-new.jpg"
	return "http://lh5.ggpht.com/_83T8aHezR6o/Sdg71WNTQsI/AAAAAAAADy0/-3mSEkiizhE/s800/BIN-new.jpg"

def getAllItemsPic():
	#return "http://lh4.ggpht.com/_83T8aHezR6o/Sdg71tFlcSI/AAAAAAAADy8/wt9AmiDP2sA/ViewAll-new.jpg"
	return "http://lh4.ggpht.com/_83T8aHezR6o/Sdg71tFlcSI/AAAAAAAADy8/wt9AmiDP2sA/s800/ViewAll-new.jpg"

class BuyItNow(webapp.RequestHandler):
	def get(self):	
		referer = getReferer(request=self.request)
		if referer == None:
			#logging.debug(self.request.headers)
			return self.redirect(getAllItemsUrl())
		
		item_ebay_id = get_ebay_id_from_referer(referer=referer)
		if item_ebay_id == None:
			#logging.debug(self.request.headers)
			return self.redirect(getAllItemsUrl())
		
		item = getActiveItem(ebay_id=item_ebay_id)
		if item == None or item.format == 2:
			#logging.debug(self.request.headers)
			return self.redirect(getAllItemsUrl())
		
		store_id = item.store_id
		item = getBinItem(store_id=store_id)
		if item == None:
			#logging.debug(self.request.headers)
			return self.redirect(getAllItemsUrl())
		return self.redirect("http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item=" + item.ebay_id)
	
class BuyItNowPic(webapp.RequestHandler):
	def get(self):
		referer = getReferer(request=self.request)
		if referer == None:
			return self.redirect(getAllItemsPic())
		item_ebay_id = get_ebay_id_from_referer(referer=referer)
		if item_ebay_id == None:
			return self.redirect(getAllItemsPic())
		
		item = getActiveItem(ebay_id=item_ebay_id)
		if item == None or item.format == 2:
			return self.redirect(getAllItemsPic())
		
		store_id = item.store_id
		item = getBinItem(store_id=store_id)
		if item == None:
			return self.redirect(getAllItemsPic())
		return self.redirect(getBinPic())

class BuildItems(webapp.RequestHandler):
    def get(self):
        build_hot_items();
        build_newly_listed_items();
        memcache.delete(FLAG_UPDATING) # updating process ends, delete updating flag
        return self.response.out.write("OK")
        
class PrepareUpdate(webapp.RequestHandler): # set ready_to_delete to True
    def get(self):
        each_rount = 50
        memcache.set(FLAG_UPDATING, True,  max_updating_duration) # updating process starts, set updating flag
        results = db.Query(ItemModel).filter('ready_to_delete = ', False).fetch(each_rount)
        for i in results:
            i.ready_to_delete = True
        db.put(results)
        count = db.Query(ItemModel).filter('ready_to_delete = ', False).count()
        return self.response.out.write(str(count))

application = webapp.WSGIApplication(
                                     [  ('/', MainPage),
                                        ('/item', RankedItem),
                                        ('/build',  BuildItems), 
                                        ('/image', RankedImage),
                                        ('/delete', DeleteOldItems),
                                        ('/update', UpdateItem),
                                        ('/binitem', BuyItNow),
                                        ('/binpic', BuyItNowPic), 
                                        ('/prepareupdate',  PrepareUpdate)                                        
                                      ],
                                     debug=True)

def main():
	run_wsgi_app(application)

if __name__ == "__main__":
	main()
